﻿using System;
using System.ServiceModel;
using UI.Process.ServiceReference;

namespace UI.Process
{
    public delegate void GetPersonAsyncCompletedEventHandler(object sender, PersonDto[] persons);

    public class PersonProcess
    {
        private string ClientTag { get; set; }
        private string Token { get; set; }
        private readonly ServiceClient _client;
        public event GetPersonAsyncCompletedEventHandler GetPersonAsyncCompleted;

        public PersonProcess(string tag, string token)
        {
            ClientTag = tag;
            Token = token;
            _client = new ServiceClient();
        }

        #region Sync

        public PersonDto[] GetAll()
        {
            return ProcessGetSync(MakeRequest(new PersonCriteria { Options = PersonOptions.All }));
        }

        public PersonDto[] GetById(string id)
        {
            return ProcessGetSync(MakeRequest(new PersonCriteria { Options = PersonOptions.Id, Value = id }));
        }

        private PersonDto[] ProcessGetSync(PersonGetRequest request)
        {
            PersonDto[] result = null;
            try
            {
                var getPersonResponseSync = _client.GetPerson(request);
                if (getPersonResponseSync.ResponseHeader.Status == OperationStatus.Success)
                {
                    result = getPersonResponseSync.Objects;
                }
            }
            catch (TimeoutException ex)
            {
                throw new Exception("Timeout", ex);
            }
            catch (FaultException<ServiceFault> ex)
            {
                throw new Exception("ServiceFault", ex);
            }
            return result;
        }

        #endregion

        #region Async Server

        public void GetAllServerAsync()
        {
            ProcessGetServerAsync(MakeRequest(new PersonCriteria { Options = PersonOptions.All }));
        }

        public void GetByIdServerAsync(string id)
        {
            ProcessGetServerAsync(MakeRequest(new PersonCriteria { Options = PersonOptions.Id, Value = id }));
        }

        private void ProcessGetServerAsync(PersonGetRequest request)
        {
            _client.BeginGetPerson(request, OnEndGetPerson, null);
        }

        private void OnEndGetPerson(IAsyncResult ar)
        {
            var response = _client.EndGetPerson(ar);
            OnGetPersonAsyncCompleted(response.ResponseHeader.Status == OperationStatus.Success
                                               ? response.Objects
                                               : null);
        }

        #endregion

        #region Async Client

        public void GetAllClientAsync()
        {
            ProcessGetClientAsync(MakeRequest(new PersonCriteria { Options = PersonOptions.All }));
        }

        public void GetByIdClientAsync(string id)
        {
            ProcessGetClientAsync(MakeRequest(new PersonCriteria { Options = PersonOptions.Id, Value = id }));
        }

        private void ProcessGetClientAsync(PersonGetRequest request)
        {
            _client.GetPersonCompleted += GetPersonCallback;
            _client.GetPersonAsync(request);
        }

        private void GetPersonCallback(object sender, GetPersonCompletedEventArgs e)
        {
            var response = e.Result;
            if (response.ResponseHeader.Status == OperationStatus.Success)
                OnGetPersonAsyncCompleted(response.ResponseHeader.Status == OperationStatus.Success
                                             ? response.Objects
                                             : null);
        }

        #endregion

        private void OnGetPersonAsyncCompleted(PersonDto[] persons)
        {
            if (GetPersonAsyncCompleted != null)
            {
                GetPersonAsyncCompleted(this, persons);
            }
        }

        private PersonGetRequest MakeRequest(PersonCriteria criteria)
        {
            return new PersonGetRequest
            {
                RequestHeader = new RequestHeader
                {
                    ClientTag = ClientTag,
                    RequestId = Guid.NewGuid().ToString(),
                    SecurityToken = Token
                },
                Criteria = criteria
            };
        }
    }
}
